package com.siyu.model.dispatch.algorithm;


import com.siyu.model.dispatch.algorithm.interfaces.SingleReservoirInterface;
import com.siyu.model.dispatch.io.SingleDamInput;
import com.siyu.model.dispatch.io.SingleDamOutput;
import com.siyu.model.dispatch.utils.CalculateUtils;
import com.siyu.model.dispatch.utils.CurveDataItem;
import com.siyu.model.dispatch.utils.ReserviorTimeResult;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class MinimumDischargeSingleReservoir implements SingleReservoirInterface {
    // 说明：防洪调度优化方法，最大削峰准则，保护下游安全
    private String reservoirName;  // 水库名称

    private String reservoirID;  // 水库ID

    private String startTime;  // 起始时间

    private String strResCode;  // 水库编号 用于提取对应水库的数据时使用

    private double deadLevel;  // 死水位

    private double SJWaterLevel;  // 设计水位

    private double floodLevel;  // 汛限水位

    private int numOperationYear = 1;  // 长系列年数

    private int numPeriod;  // 一年内的时段数

    private int totalNumPeriod;  // 长系列所有时段总数

    private int PeriodLength;  // 时段长度 单位（s）

    private double[][] operationRunoffData;  // 长系列径流过程

    private double[][] intervalRunoffData;  // 下游区间的径流过程

    double[] UpLevelLimitArray;  // 各时段水位上限

    double[] DownLevelLimitArray;  // 各时段水位下限

    double[] UpVolumeLimitArray;  // 各时段库容上限

    double[] DownVolumeLimitArray;  // 各时段库容下限

    double[] UpQreleaseLimitArray;  // 各时段下泄流量上限

    double[] DownQreleaseLimitArray;  // 各时段下泄流量下限

    double[] EvaLoss;  // 全年各时段蒸发量 单位：m^3/s

    int CurveExPLength;  // 预想出力曲线点数

    double[][] CurveExP;  // 预想出力曲线 水头(m)、限制出力值(KW)

    int CurveLVLength;  // 水位库容关系曲线点数

    double[][] CurveLV;  // 水位库容关系曲线 水位(m)、库容(亿m^3)

    int CurveHRLength;  // 水头耗水率关系曲线点数

    double[][] CurveHR;  // 水头耗水率关系曲线 水头(m)、耗水率(m^3/kWh)

    int CurveLQLength;  // 泄流能力关系曲线点数

    double[][] CurveLQ;  // 泄流能力关系曲线 水位(m)、流量(m^3/s)

    int CurveDLQLength;  // 下游水位流量关系曲线点数

    double[][] CurveDLQ;  // 下游水位流量关系曲线 水位(m)、流量(m^3/s)

    double[][][] OptimalRemainBenefit;  // 某时段对应时段初某库容离散点的最优余留效益

    double[] TempOptimalRemainBenefit;  // 用于暂时保存一个时段对应时段初某库容离散点的最优余留效益

    double[][][] OptimalRemainVolume;  // 某时段对应时段初某库容离散点的最优余留效益所对应的时段末库容

    double[][][] OptimalRemainQrelease;  // 某时段对应时段初某库容离散点的最优余留效益所对应的时段下泄流量

    int[][][] OptimalVolumePoints;  // 记录最优路径的各时段末的库容离散点

    double[][] OptimalLine_Volume;  // 最优调度线上各库容点值

    double[][] OptimalLine_Level;  // 最优调度线上的水位

    private double[][] OptimalLine_Qrelease;  // 最优调度线上各时段下泄流量

    double[][] OptimalLine_RemainBenefit;  // 最优调度线上各时段的余留效益（电量）

    int[][] OptimalLine_VolumePoints;  // 最优调度线上各离散库容点的标号

    int DiscretePointNum = 10;  // 库容离散点数

    double DiscretePointRate = 1;

    int[][] DiscretePointNumofEachStage;

    double MinimumBenefit = 0;  // 长系列计算最小余留效益

    double FloodPeak = 0;  // 入库洪峰 - max{长系列径流过程}

    double FloodVolume = 0;  // 入库洪量

    double FloodDischarge = 0;  // 最大泄洪

    double MaxLevel = 0;  // 最高水位

    double FloodRetention = 0;  // 拦蓄洪量

    double PeakClippingRate = 0;  // 削峰率

    private String folderPath;  // 输入数据文件路径

    private double meanQrelease;  // 平均下泄

    private double meanLevel;  // 平均水位

    private double InitialLevel;  // 初始水位

    private String RKPeakTime;  // 入库峰现时间

    private String MaxLevelTime;  // 最高水位出现时间

    private String CKPeakTime; // 出库峰现时间

    private boolean MaxLevelExceedStandard;  // 最高水位是否超标准

    private boolean MaxLevelExceedLimit;  // 最高水位是否超汛限

    private boolean EndLevelExceedLimit;  // 末水位是否超过汛期

    private double CKVolume;  // 出库洪量

    private double EndLevel; // 期末水位

    private double[] tempQ;  // TODO: 待补充定义

    private MuskingenMethod oneChannel;  // 马斯京跟计算对象

//    private SingleDamInput singleDamInput;  // 单水库输入类对象

    private SingleDamOutput singleDamOutput;  // 单水库结果

    private double designWaterLevel;  // 设计水位

    /*
     * 默认构造器
     */
    public MinimumDischargeSingleReservoir() {

    }

    /*
     * 有参构造器
     */
    public MinimumDischargeSingleReservoir(SingleDamInput singleDamInput, String folderPath, String strResCode, MuskingenMethod oneChannel) {
        this.folderPath = folderPath;
        this.strResCode = strResCode;

        // TODO: 这些值其实在水库输入类中都已存在，可以不必再次存储
        reservoirName = singleDamInput.getReservoirName();
        reservoirID = singleDamInput.getReservoirID();
        deadLevel = singleDamInput.getDeadLevel();
        SJWaterLevel = singleDamInput.getDesignWaterLevel();
        floodLevel = singleDamInput.getFloodLevel();
        FloodPeak = singleDamInput.getFloodPeak();
        FloodVolume = singleDamInput.getFloodVolume();
        numPeriod = singleDamInput.getNumPeriod();
        PeriodLength = singleDamInput.getPeriodLength();
        DiscretePointRate = singleDamInput.getDiscretePointRate();
        startTime = singleDamInput.getStartTime();
        designWaterLevel = singleDamInput.getDesignWaterLevel();

        totalNumPeriod = numOperationYear * numPeriod;
        operationRunoffData = new double[numOperationYear][numPeriod];
        intervalRunoffData = new double[numOperationYear][numPeriod];

        List<Double> TempOperationRunoffData = singleDamInput.getOperationRunoffData();
        for (int j = 0; j < TempOperationRunoffData.size(); j++) {
            operationRunoffData[0][j] = TempOperationRunoffData.get(j);
        }

        List<Double> TempIntervalRunoffData = singleDamInput.getIntervalRunoffData();
        for (int j = 0; j < TempIntervalRunoffData.size(); j++) {
            intervalRunoffData[0][j] = TempIntervalRunoffData.get(j);
        }


        UpLevelLimitArray = new double[numPeriod + 1];
        DownLevelLimitArray = new double[numPeriod + 1];

        List<Double> TempUpLevelLimitArray = singleDamInput.getUpLevelLimitArray();
        for (int j = 0; j < TempUpLevelLimitArray.size(); j++) {
            UpLevelLimitArray[j] = TempUpLevelLimitArray.get(j);
        }

        List<Double> TempDownLevelLimitArray = singleDamInput.getDownLevelLimitArray();
        for (int j = 0; j < TempDownLevelLimitArray.size(); j++) {
            DownLevelLimitArray[j] = TempDownLevelLimitArray.get(j);
        }

        UpQreleaseLimitArray = new double[numPeriod];
        DownQreleaseLimitArray = new double[numPeriod];

        List<Double> TempUpQreleaseLimitArray = singleDamInput.getUpQreleaseLimitArray();
        for (int j = 0; j < TempUpQreleaseLimitArray.size(); j++) {
            UpQreleaseLimitArray[j] = TempUpQreleaseLimitArray.get(j);
        }

        List<Double> TempDownQreleaseLimitArray = singleDamInput.getDownQreleaseLimitArray();
        for (int j = 0; j < TempDownQreleaseLimitArray.size(); j++) {
            DownQreleaseLimitArray[j] = TempDownQreleaseLimitArray.get(j);
        }

        EvaLoss = new double[numPeriod];

        List<Double> TempEvaLoss = singleDamInput.getEvaLoss();
        for (int j = 0; j < TempEvaLoss.size(); j++) {
            EvaLoss[j] = TempEvaLoss.get(j);
        }

        List<CurveDataItem> TempCurveLV = singleDamInput.getCurveLV();
        CurveLVLength = TempCurveLV.size();
        CurveLV = new double[CurveLVLength][2];
        for (int j = 0; j < TempCurveLV.size(); j++) {
            CurveLV[j][0] = TempCurveLV.get(j).getX1();
            CurveLV[j][1] = TempCurveLV.get(j).getY1();
        }

        List<CurveDataItem> TempCurveLQ = singleDamInput.getCurveLQ();
        CurveLQLength = TempCurveLQ.size();
        CurveLQ = new double[CurveLQLength][2];
        for (int j = 0; j < TempCurveLQ.size(); j++) {
            CurveLQ[j][0] = TempCurveLQ.get(j).getX1();
            CurveLQ[j][1] = TempCurveLQ.get(j).getY1();
        }

        this.oneChannel = oneChannel;
    }

    public SingleDamOutput getSingleDamOutput() {
        return singleDamOutput;
    }

    public double[][] getOptimalLine_Qrelease() {
        return OptimalLine_Qrelease;
    }

    public double[][] getIntervalRunoffData() {
        return intervalRunoffData;
    }

    public int getPeriodLength() {
        return PeriodLength;
    }

    public double[] getTempQ() {
        return tempQ;
    }

    /*
     * 主要计算过程、属性赋值操作
     */
    public void calculate() throws Exception {

        numOperationYear = 1;  // 因为算短期，不会超过一年，所以这儿统一定为1

        UpVolumeLimitArray = new double[numPeriod + 1];
        DownVolumeLimitArray = new double[numPeriod + 1];

        // 获取各时段库容上下限
        for (int i = 0; i < numPeriod + 1; i++) {
            UpVolumeLimitArray[i] = CalculateUtils.getVolumeFromLV(UpLevelLimitArray[i], CurveLV, CurveLVLength);
        }
        for (int i = 0; i < numPeriod + 1; i++) {
            DownVolumeLimitArray[i] = CalculateUtils.getVolumeFromLV(DownLevelLimitArray[i], CurveLV, CurveLVLength);
        }

        // 根据来水更新水位上限，按照不下泄来确定
        for (int j = 1; j < numPeriod; j++) {  // 没有NumPeriod，因为最后一个水位为固定值
            UpVolumeLimitArray[j] = UpVolumeLimitArray[j - 1] + operationRunoffData[0][j - 1] * PeriodLength / 100000000;
            if (UpVolumeLimitArray[j] > CalculateUtils.getVolumeFromLV(SJWaterLevel, CurveLV, CurveLVLength)) {
                UpVolumeLimitArray[j] = CalculateUtils.getVolumeFromLV(SJWaterLevel, CurveLV, CurveLVLength);
            }
            UpLevelLimitArray[j] = CalculateUtils.getLevelFromLV(UpVolumeLimitArray[j], CurveLV, CurveLVLength);
        }

        // 根据下泄能力更新水位下，按最大下泄来确定(太大不可取)
        for (int j = 1; j < numPeriod; j++)  // 没有NumPeriod，因为最后一个水位为固定值
        {
            //DownVolumeLimitArray[j]=DownVolumeLimitArray[j-1]-getMaxQreleaseFromLQ(DownLevelLimitArray[j-1], CurveLQ, CurveLQLength)*PeriodLength/100000000;
            // DownVolumeLimitArray[j]=DownVolumeLimitArray[j-1];//保证水位不降低，也不合理
            DownVolumeLimitArray[j] = DownVolumeLimitArray[j - 1] - 2 * operationRunoffData[0][j - 1] * PeriodLength / 100000000;

            if (DownVolumeLimitArray[j] < CalculateUtils.getVolumeFromLV(deadLevel, CurveLV, CurveLVLength)) {
                DownVolumeLimitArray[j] = CalculateUtils.getVolumeFromLV(deadLevel, CurveLV, CurveLVLength);
            }
            DownLevelLimitArray[j] = CalculateUtils.getLevelFromLV(DownVolumeLimitArray[j], CurveLV, CurveLVLength);
        }

        DiscretePointNumofEachStage = new int[numOperationYear][numPeriod + 1];// 各个时段的初末水位离散数，动态变化的

        CalculateDiscretePointNumofEachStage();

        // 获取各个时段变离散点数的最大值,以这个值开辟数组空间，变长度数据还不知道怎么弄
        DiscretePointNum = DiscretePointNumofEachStage[0][0];
        for (int i = 0; i <= numOperationYear - 1; i++) {

            for (int j = 0; j < numPeriod + 1; j++) {
                if (DiscretePointNumofEachStage[i][j] > DiscretePointNum) {
                    DiscretePointNum = DiscretePointNumofEachStage[i][j];
                }

            }
        }

        // 开辟数组空间
        OptimalRemainBenefit = new double[numOperationYear][numPeriod][DiscretePointNum + 1];
        TempOptimalRemainBenefit = new double[DiscretePointNum + 1];
        OptimalRemainVolume = new double[numOperationYear][numPeriod][DiscretePointNum + 1];
        OptimalRemainQrelease = new double[numOperationYear][numPeriod][DiscretePointNum + 1];
        OptimalVolumePoints = new int[numOperationYear][numPeriod][DiscretePointNum + 1];
        OptimalLine_Volume = new double[numOperationYear][numPeriod];
        OptimalLine_Qrelease = new double[numOperationYear][numPeriod];
        OptimalLine_RemainBenefit = new double[numOperationYear][numPeriod];
        OptimalLine_VolumePoints = new int[numOperationYear][numPeriod];
        OptimalLine_Level = new double[numOperationYear][numPeriod];

        double[] BeginDiscreteVolume = new double[0];
        double[] EndDiscreteVolume;
        double UpVolumeLimit = 0;
        double DownVolumeLimit = 0;
        int PunishCoefficientforQreleaseImpossibleSituation = 100000000; // 下泄流量为负惩罚系数 这种情况不容发生
        int PunishCoefficientforQreleaseNotInScope = 100000; // 下泄流量不在允许范围内的惩罚系数,根据下游安全泄量确定
        // 这个惩罚要调试，对结果有影响。有时候惩罚不到

        // 逆推计算
        for (int i = numOperationYear - 1; i >= 0; i--) {
            for (int j = numPeriod - 1; j >= 0; j--) {

                System.out.println("计算到第：" + String.format("%-4s", numPeriod - j) + "个时段，一共" + String.format("%-4s", numPeriod) + "个时段");
                System.out.println("当前时段末离散点数为：" + String.format("%-15s", DiscretePointNumofEachStage[i][j + 1]));

                // 离散库容
                BeginDiscreteVolume = new double[DiscretePointNumofEachStage[i][j] + 1];
                EndDiscreteVolume = new double[DiscretePointNumofEachStage[i][j + 1] + 1];

                UpVolumeLimit = CalculateUtils.getVolumeFromLV(UpLevelLimitArray[j], CurveLV, CurveLVLength);
                DownVolumeLimit = CalculateUtils.getVolumeFromLV(DownLevelLimitArray[j], CurveLV, CurveLVLength);

                for (int k = 0; k <= DiscretePointNumofEachStage[i][j]; k++) {
                    BeginDiscreteVolume[k] = (UpVolumeLimit - DownVolumeLimit) * 100000000 * (DiscretePointNumofEachStage[i][j] - k) / DiscretePointNumofEachStage[i][j] + DownVolumeLimit * 100000000;
                }

                UpVolumeLimit = CalculateUtils.getVolumeFromLV(UpLevelLimitArray[j + 1], CurveLV, CurveLVLength);
                DownVolumeLimit = CalculateUtils.getVolumeFromLV(DownLevelLimitArray[j + 1], CurveLV, CurveLVLength);

                for (int k = 0; k <= DiscretePointNumofEachStage[i][j + 1]; k++) {
                    EndDiscreteVolume[k] = (UpVolumeLimit - DownVolumeLimit) * 100000000 * (DiscretePointNumofEachStage[i][j + 1] - k) / DiscretePointNumofEachStage[i][j + 1] + DownVolumeLimit * 100000000;
                }
                //EndDiscreteVolume单位m^3

                // 时段内计算
                for (int m = 0; m <= DiscretePointNumofEachStage[i][j]; m++) {
                    for (int n = 0; n <= DiscretePointNumofEachStage[i][j + 1]; n++) {
                        // 一个时段中 初库容点m所对应的DiscretePointNum个末库容点之间的计算
                        double Qrelease = 0;
                        double BeginPeriodLevel = 0; // 时段初水位
                        double EndPeriodLevel = 0; // 时段末水位
                        BeginPeriodLevel = CalculateUtils.getLevelFromLV(BeginDiscreteVolume[m] / 100000000, CurveLV, CurveLVLength);
                        EndPeriodLevel = CalculateUtils.getLevelFromLV(EndDiscreteVolume[n] / 100000000, CurveLV, CurveLVLength);
                        Qrelease = (BeginDiscreteVolume[m] - EndDiscreteVolume[n]) / PeriodLength + operationRunoffData[i][j] - EvaLoss[j];

                        // 根据流量约束和出力约束 分为三种情况
                        if (Qrelease < 0 || Qrelease > CalculateUtils.getMaxQreleaseFromLQ(BeginPeriodLevel / 2 + EndPeriodLevel / 2, CurveLQ, CurveLQLength))// 非负约束、下泄能力约束，这些都是不可能出现的
                        {
                            // getMaxQreleaseFromLQ(BeginPeriodLevel,CurveLQ, CurveLQLength))计算中应该用平均水位
                            if (j < numPeriod - 1) {
                                TempOptimalRemainBenefit[n] = PunishCoefficientforQreleaseImpossibleSituation * PunishCoefficientforQreleaseImpossibleSituation + OptimalRemainBenefit[i][j + 1][n];
                            } else if (j == numPeriod - 1 && i + 1 <= numOperationYear - 1) {
                                TempOptimalRemainBenefit[n] = PunishCoefficientforQreleaseImpossibleSituation * PunishCoefficientforQreleaseImpossibleSituation + OptimalRemainBenefit[i + 1][0][n];
                            } else if (j == numPeriod - 1 && i == numOperationYear - 1) {
                                TempOptimalRemainBenefit[n] = PunishCoefficientforQreleaseImpossibleSituation * PunishCoefficientforQreleaseImpossibleSituation + 0;
                            }

                        } else if (Qrelease < DownQreleaseLimitArray[j] || Qrelease > UpQreleaseLimitArray[j])// 下游安全泄量约束
                        {

                            if (j < numPeriod - 1) {
                                TempOptimalRemainBenefit[n] = PunishCoefficientforQreleaseNotInScope * PunishCoefficientforQreleaseNotInScope + OptimalRemainBenefit[i][j + 1][n];
                            } else if (j == numPeriod - 1 && i + 1 <= numOperationYear - 1) {
                                TempOptimalRemainBenefit[n] = PunishCoefficientforQreleaseNotInScope * PunishCoefficientforQreleaseNotInScope + OptimalRemainBenefit[i + 1][0][n];
                            } else if (j == numPeriod - 1 && i == numOperationYear - 1) {
                                TempOptimalRemainBenefit[n] = PunishCoefficientforQreleaseNotInScope * PunishCoefficientforQreleaseNotInScope + 0;
                            }

                        } else {

                            if (j < numPeriod - 1) {
//                                TempOptimalRemainBenefit[n] = 0.7*1000*Math.abs(Math.abs(Qrelease) + intervalRunoffData[i][j]-meanQrelease)/Math.abs(meanQrelease)+0.3*100000*Math.abs(EndPeriodLevel-meanLevel)/Math.abs(meanLevel) + OptimalRemainBenefit[i][j + 1][n];
//                                System.out.println(100*Math.abs(Math.abs(Qrelease) + intervalRunoffData[i][j]-meanQrelease)/Math.abs(meanQrelease));
//                                System.out.println(100000*Math.abs(EndPeriodLevel-meanLevel)/Math.abs(meanLevel));
//                                TempOptimalRemainBenefit[n] = (EndDiscreteVolume[n] + (operationRunoffData[i][j]-Math.abs(Qrelease))*PeriodLength) * (EndDiscreteVolume[n] + (operationRunoffData[i][j]-Math.abs(Qrelease))*PeriodLength)/1000000000 + OptimalRemainBenefit[i][j + 1][n];
                                TempOptimalRemainBenefit[n] = (Math.abs(Qrelease) + intervalRunoffData[i][j]) * (Math.abs(Qrelease) + intervalRunoffData[i][j]) + OptimalRemainBenefit[i][j + 1][n];
                            } else if (j == numPeriod - 1 && i + 1 <= numOperationYear - 1) {
//                                TempOptimalRemainBenefit[n] = 0.7*1000*Math.abs(Math.abs(Qrelease) + intervalRunoffData[i][j]-meanQrelease)/Math.abs(meanQrelease)+0.3*100000*Math.abs(EndPeriodLevel-meanLevel)/Math.abs(meanLevel) + OptimalRemainBenefit[i + 1][0][n];
//                                TempOptimalRemainBenefit[n] = (EndDiscreteVolume[n] + (operationRunoffData[i][j]-Math.abs(Qrelease))*PeriodLength) * (EndDiscreteVolume[n] + (operationRunoffData[i][j]-Math.abs(Qrelease))*PeriodLength)/1000000000 + OptimalRemainBenefit[i + 1][0][n];
                                TempOptimalRemainBenefit[n] = (Math.abs(Qrelease) + intervalRunoffData[i][j]) * (Math.abs(Qrelease) + intervalRunoffData[i][j]) + OptimalRemainBenefit[i + 1][0][n];
                            } else if (j == numPeriod - 1 && i == numOperationYear - 1) {
//                                TempOptimalRemainBenefit[n] = 0.7*1000*Math.abs(Math.abs(Qrelease) + intervalRunoffData[i][j]-meanQrelease)/Math.abs(meanQrelease)+0.3*100000*Math.abs(EndPeriodLevel-meanLevel)/Math.abs(meanLevel) + 0;
//                                TempOptimalRemainBenefit[n] = (EndDiscreteVolume[n] + (operationRunoffData[i][j]-Math.abs(Qrelease))*PeriodLength) * (EndDiscreteVolume[n] + (operationRunoffData[i][j]-Math.abs(Qrelease))*PeriodLength)/1000000000 + 0;
                                TempOptimalRemainBenefit[n] = (Math.abs(Qrelease) + intervalRunoffData[i][j]) * (Math.abs(Qrelease) + intervalRunoffData[i][j]) + 0;
                            }

                        }
                    }

                    // 保存时段初第m个库容离散点所对应的最优时段末库容点、最优余留效益以及其他相关信息

                    OptimalRemainBenefit[i][j][m] = CalculateUtils.getMinValue(TempOptimalRemainBenefit, DiscretePointNumofEachStage[i][j + 1]);
                    int MinIndex = CalculateUtils.getMinValueIndex(TempOptimalRemainBenefit, DiscretePointNumofEachStage[i][j + 1]);
                    OptimalVolumePoints[i][j][m] = MinIndex;
                    OptimalRemainVolume[i][j][m] = EndDiscreteVolume[MinIndex];
                    OptimalRemainQrelease[i][j][m] = (BeginDiscreteVolume[m] - OptimalRemainVolume[i][j][m]) / PeriodLength + operationRunoffData[i][j] - EvaLoss[j];

                    // 时段内局部变量清零
                    for (int k = 0; k <= DiscretePointNumofEachStage[i][j]; k++) {
                        TempOptimalRemainBenefit[k] = 0;
                    }

                }
            }
        }

        // 顺推计算求最优过程线

        // 找出全过程最优所对应的第0个时段初的库容点值

        UpVolumeLimit = CalculateUtils.getVolumeFromLV(UpLevelLimitArray[0], CurveLV, CurveLVLength);
        DownVolumeLimit = CalculateUtils.getVolumeFromLV(DownLevelLimitArray[0], CurveLV, CurveLVLength);
        for (int k = 0; k <= DiscretePointNumofEachStage[0][0]; k++) {
            BeginDiscreteVolume[k] = (UpVolumeLimit - DownVolumeLimit) * 100000000 * (DiscretePointNumofEachStage[0][0] - k) / DiscretePointNumofEachStage[0][0] + DownVolumeLimit * 100000000;
        }

        // 先假设全过程最优发电量所对应的第0个时段初的库容点为index=0的点

        MinimumBenefit = OptimalRemainBenefit[0][0][0];
        OptimalLine_VolumePoints[0][0] = 0;
        OptimalLine_Volume[0][0] = BeginDiscreteVolume[0];
        OptimalLine_Qrelease[0][0] = OptimalRemainQrelease[0][0][0];
        OptimalLine_RemainBenefit[0][0] = MinimumBenefit;

        for (int m = 1; m <= DiscretePointNumofEachStage[0][0]; m++) {
            if (MinimumBenefit > OptimalRemainBenefit[0][0][m]) {
                MinimumBenefit = OptimalRemainBenefit[0][0][m];
                OptimalLine_VolumePoints[0][0] = m;
                OptimalLine_Volume[0][0] = BeginDiscreteVolume[m];
                OptimalLine_Qrelease[0][0] = OptimalRemainQrelease[0][0][m];
                OptimalLine_RemainBenefit[0][0] = MinimumBenefit;
            }

        }

        int BestIndex = OptimalLine_VolumePoints[0][0];

        for (int i = 0; i <= numOperationYear - 1; i++) {
            for (int j = 0; j <= numPeriod - 1; j++) {
                // 这个地方，没有j = numPeriod，因为此时下面OptimalLine_VolumePoints[i][j]是没有的
                // 一个时段内计算
                if (j == 0 && i > 0) {
                    OptimalLine_Volume[i][j] = OptimalRemainVolume[i - 1][numPeriod - 1][BestIndex];
                    BestIndex = OptimalVolumePoints[i - 1][numPeriod - 1][BestIndex];
                    OptimalLine_VolumePoints[i][j] = BestIndex;
                    OptimalLine_Qrelease[i][j] = OptimalRemainQrelease[i][j][BestIndex];
                    OptimalLine_RemainBenefit[i][j] = OptimalRemainBenefit[i][j][BestIndex];
                } else if (j == 0 && i == 0) {
                    // 即第一个时段的计算 前面已经确定
                } else if (j > 0 && i >= 0) {
                    OptimalLine_Volume[i][j] = OptimalRemainVolume[i][j - 1][BestIndex];
                    BestIndex = OptimalVolumePoints[i][j - 1][BestIndex];
                    OptimalLine_VolumePoints[i][j] = BestIndex;
                    OptimalLine_Qrelease[i][j] = OptimalRemainQrelease[i][j][BestIndex];
                    OptimalLine_RemainBenefit[i][j] = OptimalRemainBenefit[i][j][BestIndex];
                }
            }
        }

        //由最优库容过程计算最优水位过程
        for (int i = 0; i < numOperationYear; i++) {
            for (int j = 0; j < numPeriod; j++) {
                OptimalLine_Level[i][j] = CalculateUtils.getLevelFromLV(OptimalLine_Volume[i][j] / 100000000, CurveLV, CurveLVLength);
            }
        }

        // 获取最大泄洪
        FloodDischarge = CalculateUtils.getMaxValueOfMatrix(OptimalLine_Qrelease);

        // 获取最高水位
        MaxLevel = CalculateUtils.getMaxValueOfMatrix(OptimalLine_Level);

        // 获取拦蓄水量
        FloodRetention = CalculateUtils.getVolumeFromLV(MaxLevel, CurveLV, CurveLVLength) - CalculateUtils.getVolumeFromLV(OptimalLine_Level[0][0], CurveLV, CurveLVLength);

        // 获取削峰率
        PeakClippingRate = (FloodPeak - FloodDischarge) / FloodPeak * 100;

        int timeSpan1 = CalculateUtils.getMaxValueOfMatrixIndex(operationRunoffData);
        int timeSpan2 = CalculateUtils.getMaxValueOfMatrixIndex(OptimalLine_Level);
        int timeSpan3 = CalculateUtils.getMaxValueOfMatrixIndex(OptimalLine_Qrelease);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = simpleDateFormat.parse(startTime);
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan1);
        Date date1 = instance.getTime();
        this.RKPeakTime = simpleDateFormat.format(date1);

        instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan2);
        Date date2 = instance.getTime();
        this.MaxLevelTime = simpleDateFormat.format(date2);

        instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan3);
        Date date3 = instance.getTime();
        this.CKPeakTime = simpleDateFormat.format(date3);

        // 计算 最高水位是否超标准、最高水位是否超汛限、末水位是否超汛限
        this.MaxLevelExceedStandard = MaxLevel > SJWaterLevel;
        this.MaxLevelExceedLimit = MaxLevel > floodLevel;
        this.EndLevelExceedLimit = OptimalLine_Level[0][numPeriod - 1] > floodLevel;

        // 计算 出库洪量、期末水位、调度期末水位
        this.CKVolume = FloodVolume - FloodRetention;
        this.EndLevel = OptimalLine_Level[0][numPeriod - 1];
        this.InitialLevel = OptimalLine_Level[0][0];

        // 写入单水库结果和单闸坝结果
        this.singleDamOutput = createSingleDamOutput();

        // 马斯京跟河道演算
        tempQ = oneChannel.msjg(getOptimalLine_Qrelease()[0]);
    }

    public void CalculateDiscretePointNumofEachStage() {

        // 根据来流量大小确定离散程度，否者离散度太大时，及时当前时段的全部来水用来蓄水，也达不到一个离散区间的要求，这样就会出现负的下泄流量

        // 获取时段来流来流最小
//        int CalculatedDiscretePointNum = 0;
//        CalculatedDiscretePointNum = (int) ((UpVolumeLimitArray[0] - DownVolumeLimitArray[0]) * 100000000 / (operationRunoffData[0][0] * PeriodLength));
//        DiscretePointNumofEachStage[0][0] = (int) (CalculatedDiscretePointNum * DiscretePointRate);
//        if (DiscretePointNumofEachStage[0][0] == 0) {
//            DiscretePointNumofEachStage[0][0] = 10;
//        }

        for (int i = 0; i <= numOperationYear - 1; i++) {
            for (int j = 0; j <= numPeriod; j++) {
//            for (int j = 1; j <= numPeriod; j++) {
//                CalculatedDiscretePointNum = (int) ((UpVolumeLimitArray[j] - DownVolumeLimitArray[j]) * 100000000 / (operationRunoffData[i][j - 1] * PeriodLength));
                DiscretePointNumofEachStage[i][j] = 1000;
//                DiscretePointNumofEachStage[i][j] = (int) (CalculatedDiscretePointNum * DiscretePointRate);
//                if (DiscretePointNumofEachStage[i][j] == 0) {
//                    DiscretePointNumofEachStage[i][j] = 10;
//                }
            }
        }
    }

    /**
     * 将计算出的结果写入到 SingleDamOutput 属性
     *
     * @return
     * @throws Exception
     */
    public SingleDamOutput createSingleDamOutput() throws Exception {
        SingleDamOutput singleDamOutput = new SingleDamOutput();

        List<ReserviorTimeResult> timeDataList = new ArrayList<>();  // 时间,时段编号

        List<Double> TempOperationRunoffData = new ArrayList<>();  // 入库来水

        List<Double> TempUpLevelLimitArray = new ArrayList<>();  // 各个时段水位上限

        List<Double> TempDownLevelLimitArray = new ArrayList<>();  // 各个时段水位下限

        List<Double> TempUpQreleaseLimitArray = new ArrayList<>();  // 各时段下泄流量上限

        List<Double> TempDownQreleaseLimitArray = new ArrayList<>();  // 各时段下泄流量下限

        List<Double> TempOptimalLine_Volume = new ArrayList<>();  // 最优过程的时段初库容

        List<Double> TempOptimalLine_Level = new ArrayList<>();  // 最优过程的时段初水位

        List<Double> TempMaxQreleaseFromLQ = new ArrayList<>();  // 各时段最大泄流能力

        List<Double> TempOptimalLine_Qrelease = new ArrayList<>();  // 各时段下泄流量

        List<Double> TempIntervalRunoffData = new ArrayList<>();  // 各时段区间入流

        List<Double> TempDownstreamSectionInflow = new ArrayList<>();  // 下游断面各时段来流

        ReserviorTimeResult Temp = new ReserviorTimeResult();

        for (int j = 0; j < numPeriod; j++) {
            Temp.setTime(String.valueOf(j));
            timeDataList.add(Temp);
            Temp = new ReserviorTimeResult();
        }

        singleDamOutput.setTimeDataList(timeDataList);

        for (int j = 0; j < numPeriod; j++) {
            TempOperationRunoffData.add(operationRunoffData[0][j]);
        }
        singleDamOutput.setOperationRunoffData(TempOperationRunoffData);

        for (int j = 0; j < numPeriod; j++) {
            TempUpLevelLimitArray.add(UpLevelLimitArray[j]);
        }
        singleDamOutput.setUpLevelLimitArray(TempUpLevelLimitArray);

        for (int j = 0; j < numPeriod; j++) {
            TempDownLevelLimitArray.add(DownLevelLimitArray[j]);
        }
        singleDamOutput.setDownLevelLimitArray(TempDownLevelLimitArray);

        for (int j = 0; j < numPeriod; j++) {
            TempMaxQreleaseFromLQ.add(CalculateUtils.getMaxQreleaseFromLQ(OptimalLine_Level[0][j], CurveLQ, CurveLQLength));
        }
        singleDamOutput.setMaxQreleaseFromLQ(TempMaxQreleaseFromLQ);

        for (int j = 0; j < numPeriod; j++) {
            TempUpQreleaseLimitArray.add(UpQreleaseLimitArray[j]);
        }
        singleDamOutput.setUpQreleaseLimitArray(TempUpQreleaseLimitArray);

        for (int j = 0; j < numPeriod; j++) {
            TempDownQreleaseLimitArray.add(DownQreleaseLimitArray[j]);
        }
        singleDamOutput.setDownQreleaseLimitArray(TempDownQreleaseLimitArray);

        for (int j = 0; j < numPeriod; j++) {
            TempOptimalLine_Level.add(OptimalLine_Level[0][j]);
        }
        singleDamOutput.setOptimalLine_Level(TempOptimalLine_Level);

        for (int j = 0; j < numPeriod; j++) {
            TempOptimalLine_Volume.add(OptimalLine_Volume[0][j] / 100000000);
        }
        singleDamOutput.setOptimalLine_Volume(TempOptimalLine_Volume);

        for (int j = 0; j < numPeriod; j++) {
            TempOptimalLine_Qrelease.add(OptimalLine_Qrelease[0][j]);
        }
        singleDamOutput.setOptimalLine_Qrelease(TempOptimalLine_Qrelease);

        singleDamOutput.setFloodPeak(FloodPeak);

        singleDamOutput.setFloodVolume(FloodVolume);

        singleDamOutput.setFloodDischarge(FloodDischarge);

        singleDamOutput.setFloodRetention(FloodRetention);

        singleDamOutput.setPeakClippingRate(PeakClippingRate);

        String Judge1 = MaxLevelExceedStandard ? "最高水位超标准" : "最高水位不超标准";
        String Judge2 = MaxLevelExceedLimit ? "最高水位超汛限" : "最高水位不超汛限";
        String Judge3 = EndLevelExceedLimit ? "期末水位超汛限" : "期末水位不超汛限";

        singleDamOutput.setRKPeakTime(RKPeakTime);

        singleDamOutput.setMaxLevelTime(MaxLevelTime);

        singleDamOutput.setCKPeakTime(CKPeakTime);

        String suggestionString = String.format("%s %s %.1f %s %d %s %.2f %s %s %s %.2f %s %s %s %.1f %s %s %s %s %s %s "
                + "%.2f %s %.2f %s %.1f %s %s", reservoirName, "调度建议：初始水位为", OptimalLine_Level[0][0], "米，未来",
                numPeriod, "h内水库最大入库流量为", FloodPeak, "立方米/秒，入库峰现时间为", RKPeakTime, "入库洪水总量为",
                FloodVolume, "亿立方米，预计水库将会在", MaxLevelTime, "达到调洪最高水位", MaxLevel, "米",
                Judge1, Judge2, "水库控制过程中为在保证水库安全的前提下保证下游河道安全，下泄流量在", CKPeakTime, "达到峰值", FloodDischarge,
                "立方米/秒，出库洪水总量为", CKVolume, "亿立方米，期末水位为", EndLevel, "米", Judge3);
        singleDamOutput.setSuggestion(suggestionString);

        for (int j = 0; j < numPeriod; j++) {
            TempIntervalRunoffData.add(intervalRunoffData[0][j]);
        }
        singleDamOutput.setIntervalRunoffData(TempIntervalRunoffData);

        for (int j = 0; j < numPeriod; j++) {
            TempDownstreamSectionInflow.add(OptimalLine_Qrelease[0][j] + intervalRunoffData[0][j]);
        }
        singleDamOutput.setDownstreamSectionInflow(TempDownstreamSectionInflow);

        double[] weight = AHPComputeWeight.getWeight();
        double grade = weight[0] * (UpLevelLimitArray[numPeriod - 1] / MaxLevel) * 90 + weight[1] * (1 - (CalculateUtils.getVolumeFromLV(OptimalLine_Level[0][numPeriod - 1], CurveLV, CurveLVLength) - CalculateUtils.getVolumeFromLV(floodLevel, CurveLV, CurveLVLength)) / (CalculateUtils.getVolumeFromLV(SJWaterLevel, CurveLV, CurveLVLength) - CalculateUtils.getVolumeFromLV(floodLevel, CurveLV, CurveLVLength))) * 60 + weight[2] * (DownLevelLimitArray[numPeriod - 1] / OptimalLine_Level[0][numPeriod - 1]) * 60 + weight[3] * (UpQreleaseLimitArray[numPeriod - 1] / FloodDischarge) * 4  //百年一遇以上乘以80
                + weight[4] * (UpQreleaseLimitArray[numPeriod - 1] / FloodDischarge) * 0.5;  //百年一遇以上乘以10
        singleDamOutput.setGrade(grade);

        /*
         * 设置 最高水位是否超标准、最高水位是否超汛限、末水位是否超汛限（3 个 boolean 值）
         */
        singleDamOutput.setMaxLevelExceedStandard(MaxLevelExceedStandard);
        singleDamOutput.setMaxLevelExceedLimit(MaxLevelExceedLimit);
        singleDamOutput.setEndLevelExceedLimit(EndLevelExceedLimit);

        /*
         * 设置 入库洪量、调度期末水位、初始水位
         */
        singleDamOutput.setCKVolume(CKVolume);
        singleDamOutput.setEndLevel(EndLevel);
        singleDamOutput.setInitialLevel(InitialLevel);
        singleDamOutput.setMaxLevel(MaxLevel);

        /*
         * 计算超标准水位时段和超讯限水位时段
         */
        String overStandard = CalculateUtils.overWaterLevel(singleDamOutput.getOptimalLine_Level(), designWaterLevel, startTime);
        singleDamOutput.setOverStandardTime(overStandard);

        String overLimit = CalculateUtils.overWaterLevel(singleDamOutput.getOptimalLine_Level(), floodLevel, startTime);
        singleDamOutput.setOverLimitTime(overLimit);

        return singleDamOutput;
    }

    /**
     * 输出详细结果到 txt 文件
     *
     * @param path
     * @throws IOException
     * @throws ParseException
     */
    public void OutputResults(String path) throws IOException, ParseException {
        String OneLine;
        ArrayList<String> list = new ArrayList<>();
        DecimalFormat myDF;
        OneLine = String.format("%-11s", "时段编号") + String.format("%-11s", "上游来水") + String.format("%-11s", "水位上限") + String.format("%-11s", "水位下限") + String.format("%-11s", "时初水位") + String.format("%-11s", "时初库容") + String.format("%-11s", "下泄下限") + String.format("%-11s", "下泄上限") + String.format("%-11s", "泄流能力") + String.format("%-11s", "下泄流量") + String.format("%-11s", "区间流量") + String.format("%-11s", "下游控制断面流量");
        list.add(OneLine);
        for (int i = 0; i < numOperationYear; i++) {
            for (int j = 0; j < numPeriod; j++) {
                String s1;  // String.format("%-15s", "时段编号")
                String s2;  // String.format("%-15s", "上游来水")
                String s3;  // String.format("%-15s", "水位上限")
                String s4;  // String.format("%-15s", "水位下限")
                String s5;  // String.format("%-15s", "时初水位")
                String s6;  // String.format("%-15s", "时初库容")

                String s11;  // String.format("%-15s", "下泄下限")
                String s12;  // String.format("%-15s", "下泄上限")
                String s12_1;  // String.format("%-15s", "泄流能力")
                String s13;  // String.format("%-15s", "下泄流量")
                String s14;  // String.format("%-15s", "区间流量")
                String s15;  // String.format("%-15s", "下游控制断面流量")

                myDF = new DecimalFormat("0");
                s1 = String.format("%-15s", myDF.format(i) + " " + myDF.format(j));
                myDF = new DecimalFormat("0.00");
                s2 = String.format("%-15s", myDF.format(operationRunoffData[i][j])) + "  ";
                myDF = new DecimalFormat("0.0000");
                s3 = String.format("%-15s", myDF.format(UpLevelLimitArray[j])) + "  ";
                myDF = new DecimalFormat("0.0000");
                s4 = String.format("%-15s", myDF.format(DownLevelLimitArray[j])) + "  ";
                myDF = new DecimalFormat("0.0000");
                s5 = String.format("%-15s", myDF.format(CalculateUtils.getLevelFromLV(OptimalLine_Volume[i][j] / 100000000, CurveLV, CurveLVLength))) + "  ";
                myDF = new DecimalFormat("0.0000");
                s6 = String.format("%-15s", myDF.format(OptimalLine_Volume[i][j] / 100000000)) + "  ";
                myDF = new DecimalFormat("0.0");
                s11 = String.format("%-15s", myDF.format(DownQreleaseLimitArray[j])) + "  ";
                myDF = new DecimalFormat("0.0");
                s12 = String.format("%-15s", myDF.format(UpQreleaseLimitArray[j])) + "  ";
                myDF = new DecimalFormat("0.0");
                s12_1 = String.format("%-15s", myDF.format(CalculateUtils.getMaxQreleaseFromLQ(CalculateUtils.getLevelFromLV(OptimalLine_Volume[i][j] / 100000000, CurveLV, CurveLVLength), CurveLQ, CurveLQLength))) + "   ";
                myDF = new DecimalFormat("0.00");
                s13 = String.format("%-15s", myDF.format(OptimalLine_Qrelease[i][j])) + "  ";
                myDF = new DecimalFormat("0.00");
                s14 = String.format("%-15s", myDF.format(intervalRunoffData[i][j])) + "  ";
                myDF = new DecimalFormat("0.00");
                s15 = String.format("%-15s", myDF.format(OptimalLine_Qrelease[i][j] + intervalRunoffData[i][j])) + "  ";

                OneLine = s1 + s2 + s3 + s4 + s5 + s6 + s11 + s12 + s12_1 + s13 + s14 + s15;
                list.add(OneLine);
                OneLine = "";
            }
        }

        String Judge1 = MaxLevelExceedStandard ? "最高水位超标准" : "最高水位不超标准";
        String Judge2 = MaxLevelExceedLimit ? "最高水位超汛限" : "最高水位不超汛限";
        String Judge3 = EndLevelExceedLimit ? "期末水位超汛限" : "期末水位不超汛限";

        String FloodPeakString = String.format("%s %.2f %s", "入库洪峰：", FloodPeak, " 立方米/秒");
        String FloodVolumeString = String.format("%s %.2f %s", "入库洪量：", FloodVolume, " 亿立方米");
        String FloodDischargeString = String.format("%s %.2f %s", "最大泄洪：", FloodDischarge, " 立方米/秒");
        String FloodRetentionString = String.format("%s %.2f %s", "拦蓄水量：", FloodRetention, " 亿立方米");
        String PeakClippingRateString = String.format("%s %.1f %s", "削峰率：", PeakClippingRate, "%");
        String SuggestionString = String.format("%s %s %.1f %s %d %s %.2f %s %s %s %.2f %s %s %s %.1f %s %s %s %s %s %s "
                + "%.2f %s %.2f %s %.1f %s %s", reservoirName, "调度建议：初始水位为", OptimalLine_Level[0][0], "米，未来", numPeriod,
                "h内水库最大入库流量为", FloodPeak, "立方米/秒，入库峰现时间为", RKPeakTime, "入库洪水总量为", FloodVolume,
                "亿立方米，预计水库将会在", MaxLevelTime, "达到调洪最高水位", MaxLevel, "米", Judge1, Judge2,
                "水库控制过程中为在保证水库安全的前提下保证下游河道安全，下泄流量在", CKPeakTime, "达到峰值", FloodDischarge,
                "立方米/秒，出库洪水总量为", CKVolume, "亿立方米，期末水位为", EndLevel, "米", Judge3);
        list.add(FloodPeakString);
        list.add(FloodVolumeString);
        list.add(FloodDischargeString);
        list.add(FloodRetentionString);
        list.add(PeakClippingRateString);
        list.add(SuggestionString);

        File file = new File(path);
        FileUtils.writeLines(file, "UTF-8", list);

//        // 输出上游水库区间流量结果到TXT文件
//        path = folderPath + File.separator + strResCode + File.separator + "IntervalRunoffDataofUpstreamReservoir.txt";
//        list = new ArrayList<String>();
//        OneLine = "";
//        for (int i = 0; i < numOperationYear; i++) {
//            for (int j = 0; j < numPeriod; j++) {
//                String s;  // String.format("%-15s", "下游控制断面流量")
//
//                myDF = new DecimalFormat("0.00");
//                s = myDF.format(OptimalLine_Qrelease[i][j] + intervalRunoffData[i][j]);
//                OneLine = OneLine + s + "\t";
//
//            }
//            list.add(OneLine);
//            OneLine = "";
//        }
//        file = new File(path);
//        FileUtils.writeLines(file, "UTF-8", list);
    }
}
